﻿using System;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using Dapper;


namespace Dapper.Library
{
    /// <summary>
    /// 解析查询条件
    /// </summary>
    public sealed class WhereExpression : WhereExpressionVisitor
    {
        #region sql指令
        private readonly StringBuilder _sqlCmd;
        /// <summary>
        /// sql指令
        /// </summary>
        public string SqlCmd { get; }
        /// <summary>
        /// 参数
        /// </summary>
        public new DynamicParameters Param { get; }
        #endregion

        /// <summary>
        /// 解析条件对象
        /// </summary>
        /// <param name="expression">表达式</param>
        /// <param name="prefix">参数标记</param>
        /// <param name="providerOption"></param>
        public WhereExpression(LambdaExpression expression, string prefix, SqlProvider provider) : base(provider)
        {
            this._sqlCmd = new StringBuilder(100);
            this.Param = new DynamicParameters();
            this.providerOption = provider.ProviderOption;
            //开始解析对象
            Visit(expression);
            //开始拼接成条件
            this._sqlCmd.Append(base.SpliceField);
            this.SqlCmd = " AND " + this._sqlCmd.ToString();
            if (string.IsNullOrEmpty(prefix))
            {
                this.Param.AddDynamicParams(base.Param);
            }
            else
            {
                //加上参数标记
                foreach (var paramName in base.Param.ParameterNames)
                {
                    string newName = paramName + prefix;
                    object value = base.Param.Get<object>(paramName);

                    //LW添加
                    if (value != null && DateTime.TryParse(value.ToString(), out DateTime dtDate) && provider.ProviderOption.ToString().Equals("Dapper.Library.DapperOracle.ProviderOption"))
                    {
                        string paramNameWhere = $"to_date(to_char(:{newName}, 'yyyy-mm-dd hh24:mi:ss'), 'yyyy-mm-dd hh24:mi:ss')";
                        this.SqlCmd = this.SqlCmd.Replace(":" + paramName, paramNameWhere);
                    }
                    else if (value != null && bool.TryParse(value.ToString(), out bool boolPar) && provider.ProviderOption.ToString().Equals("Dapper.Library.DapperOracle.ProviderOption"))
                    {
                        this.SqlCmd = this.SqlCmd.Replace(paramName, newName);
                        value = value.ToString().ToLower();
                    }
                    else
                    {
                        this.SqlCmd = this.SqlCmd.Replace(paramName, newName);
                    }
                    this.Param.Add(newName, value);
                }
            }
        }

        /// <summary>
        /// 是否为日期+时间型字符串
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private  bool IsDateTime(string StrSource)
        {
            return Regex.IsMatch(StrSource, @"^(((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-)) (20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d)$ ");
        }


        /// <summary>
        /// 解析二元表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression node)
        {
            var binaryWhere = new BinaryExpressionVisitor(node, base.Provider);
            this._sqlCmd.Append(binaryWhere.SpliceField);
            base.Param.AddDynamicParams(binaryWhere.Param);
            return node;
        }

        /// <summary>
        /// 解析!不等于(!里只能包含一个条件)
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Not)
            {
                this._sqlCmd.Append(" Not (");
                Visit(node.Operand);
                this._sqlCmd.Append(") ");
            }
            else
            {
                Visit(node.Operand);
            }
            return node;
        }
    }
}
